<!DOCTYPE html>
<html lang="en-US">
  <head>
    <title>Web Chat: Automated test harness</title>
    <script>
      !(function () {
        'use strict';

        const { console } = window;
        const log = (window.__console__ = []);
        const push = (type, ...args) => {
          log.push([type, args.join(' ')]);
          console[type].apply(console, args);
        };

        window.console = {
          ...console,

          debug: push.bind(null, 'debug'),
          error: push.bind(null, 'error'),
          info: push.bind(null, 'info'),
          log: push.bind(null, 'log'),
          warn: push.bind(null, 'warn')
        };

        window.addEventListener('error', ({ colno, error, filename, lineno, message }) => {
          push(
            'error',
            JSON.stringify({
              colno,
              error,
              filename,
              lineno,
              message
            })
          );
        });
      })();
    </script>
    <!--
      For demonstration purposes, we are using the latest official release of Web Chat at "/latest/webchat.js". When you are using Web Chat for production, you may lock down on a specific version with the following format: "/4.1.0/webchat.js".
    -->
    <style>
      html,
      body {
        height: 100%;
      }
      body {
        margin: 0;
      }
      input,
      textarea,
      [contenteditable] {
        caret-color: Transparent;
      }

      #manualStart {
        background-color: Transparent;
        border: 0;
        height: 100%;
        position: absolute;
        top: 0;
        width: 100%;
      }

      #webchat {
        height: 100%;
        width: 100%;
      }
    </style>
    <script>
      // Offline MockBot would collide with native ES Observable.
      // Disabling native ES Observable temporarily.
      // We should move away from legacy tests.
      window.Observable = undefined;
    </script>
    <script crossorigin="anonymous" src="https://corinagum.github.io/BotFramework-Offline-MockBot/index.js"></script>
    <script
      crossorigin="anonymous"
      src="https://unpkg.com/event-target-shim@5.0.1/dist/event-target-shim.umd.js"
    ></script>
    <script crossorigin="anonymous" src="https://unpkg.com/simple-update-in?browser"></script>
    <script crossorigin="anonymous" src="https://unpkg.com/react@16.8.6/umd/react.development.js"></script>
    <script crossorigin="anonymous" src="https://unpkg.com/react-dom@16.8.6/umd/react-dom.development.js"></script>
    <script crossorigin="anonymous" src="/createProduceConsumeBroker.js"></script>
    <script crossorigin="anonymous" src="/mockWebSpeech.js"></script>
  </head>
  <body>
    <button id="manualStart">Tap to load Web Chat</button>
    <div id="webchat" role="main"></div>
    <script>
      function unmarshal({ __evalKeys, ...obj } = {}) {
        __evalKeys &&
          __evalKeys.forEach(key => {
            obj[key] = eval(obj[key])();
          });

        return obj;
      }

      async function runHook(name, args) {
        return new Promise((resolve, reject) => {
          window.WebChatTest.store.dispatch({
            type: 'DIRECT_LINE/INCOMING_ACTIVITY',
            payload: {
              activity: {
                from: { id: 'bot' },
                id: Math.random().toString(36).substr(2, 10),
                name: 'hook',
                type: 'event',
                value: { args, name, reject, resolve }
              }
            }
          });
        });
      }

      function loadScript(src) {
        return new Promise((resolve, reject) => {
          const script = document.createElement('script');

          script.setAttribute('async', '');
          script.setAttribute('src', src);
          script.addEventListener('load', resolve);
          script.addEventListener('error', ({ error }) => reject(error));

          document.body.appendChild(script);
        });
      }

      async function retry(fn, retries) {
        let lastErr;

        for (; retries >= 0; retries--) {
          try {
            return await fn();
          } catch (err) {
            lastErr = err;
          }
        }

        throw lastErr;
      }

      const PASSTHRU_MIDDLEWARE = store => next => action => next(action);

      async function main(options) {
        document.querySelector('#manualStart').remove();

        let {
          createDirectLine,
          createStyleSet,
          disableNoMagicCode,
          props,
          setup,
          storeInitialState = {},
          storeMiddleware = PASSTHRU_MIDDLEWARE,
          useProductionBot
        } = unmarshal(options);

        props = unmarshal(props);

        if (setup) {
          await setup();
        }

        await loadScript('/webchat.js');

        let directLine;

        if (!useProductionBot && !createDirectLine) {
          directLine = window.MockBotAdapter.createDirectLine({});
        } else {
          const { token } = await retry(async () => {
            try {
              const res = await fetch('https://hawo-mockbot4-token-app.blueriver-ce85e8f0.westus.azurecontainerapps.io/api/token/directline', {
                method: 'POST',
                timeout: 2000
              });

              return await res.json();
            } catch (err) {
              console.error('Failed to fetch Direct Line token from Mockbot.');
              console.error(err);

              throw err;
            }
          }, 3);

          createDirectLine || (createDirectLine = window.WebChat.createDirectLine);
          directLine = (createDirectLine || window.WebChat.createDirectLine)({ token });
        }

        if (disableNoMagicCode) {
          directLine.getSessionId = undefined;
        }

        const store = (window.WebChatTest.store = window.WebChat.createStore(storeInitialState, store => {
          const setupMiddleware = storeMiddleware(store);

          return next => {
            const processAction = setupMiddleware(next);

            return action => {
              window.WebChatTest.actions.push(action);

              return processAction(action);
            };
          };
        }));

        const activityMiddleware = () => next => card => {
          const { activity } = card;

          if (activity.type === 'event' && activity.name === 'hook') {
            return children =>
              React.createElement(() => {
                const { args, name, reject, resolve } = activity.value;

                try {
                  const hookFn = window.WebChat.hooks[name];

                  if (!hookFn) {
                    console.log(
                      `No hooks named "${name}" were found. Valid hooks are:`,
                      Object.keys(window.WebChat.hooks)
                        .sort()
                        .map(name => `- ${name}`)
                        .join('\n')
                    );

                    throw new Error(`No hooks named "${name}" were found.`);
                  }

                  resolve(hookFn(args));
                } catch (err) {
                  reject(err);
                }

                return false;
              });
          }
          return next(card);
        };

        props = {
          activityMiddleware,
          directLine,
          store,
          styleSet: createStyleSet && createStyleSet(props.styleOptions),
          username: 'Happy Web Chat user',
          ...props
        };

        renderWebChat = props => window.WebChat.renderWebChat(props, document.getElementById('webchat'));
        renderWebChat(props);

        document.querySelector('#webchat > *').focus();

        window.WebChatTest.updateProps = mergeProps => {
          props = { ...props, ...mergeProps };

          renderWebChat(props);
        };
      }

      document.querySelector('#manualStart').addEventListener('click', () => main());

      window.WebChatTest = {
        actions: [],
        loadScript,
        runHook
      };
    </script>
  </body>
</html>
